// -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*-
//
// Copyright (c) 2006 Georgia Tech Research Corporation
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation;
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Author: George F. Riley<riley@ece.gatech.edu>
//         Gustavo Carneiro <gjc@inescporto.pt>

#include "ns3/log.h"
#include "ns3/packet.h"
#include "ns3/node.h"
#include "ns3/point-to-point-net-device.h"
#include "ns3/ipv4-route.h"
#include "ns3/nstime.h"
#include "ns3/uinteger.h"
#include "ns3/inet-socket-address.h"
#include "ns3/socket-factory.h"
#include "ns3/udp-socket-factory.h"
#include "ipv4-texcp-routing.h"
#include "texcp-header.h"
//#include "texcp-queue.h"
#include "ns3/udp-header.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/traffic-shaper-net-device.h"

/************* Constants ***************/
/// Texcp port Numer _ the same as olsr routing since texcp and olsr won't coexist _
#define TEXCP_PORT_NUMBER 698

NS_LOG_COMPONENT_DEFINE ("Ipv4TexcpRouting");

using std::make_pair;

namespace ns3 {

namespace texcp_routing {

NS_OBJECT_ENSURE_REGISTERED (Ipv4TexcpRouting);

TypeId
Ipv4TexcpRouting::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::texcp_routing::Ipv4TexcpRouting")
    .SetParent<Ipv4RoutingProtocol> ()
    .AddConstructor<Ipv4TexcpRouting> ()
    .AddAttribute("AgentType",
                  "1 for Agent 0 for core",
                  UintegerValue (1),
                  MakeUintegerAccessor (&Ipv4TexcpRouting::m_agentType),
                  MakeUintegerChecker <uint8_t> ())
    .AddAttribute ("ProbeInterval", 
                   "The time sepration interval between probes",
                   TimeValue (Seconds(0.1)),
                   MakeTimeAccessor (&Ipv4TexcpRouting::m_probeInterval),
                   MakeTimeChecker())
    .AddAttribute ("FlowletTimeOut", 
                   "The time sepration interval between probes",
                   TimeValue (Seconds(0.1)),
                   MakeTimeAccessor (&Ipv4TexcpRouting::m_flowletTimeOut),
                   MakeTimeChecker()) 
     .AddAttribute ("UpdateLossInterval", "Time interval between update of loss average.",
                   TimeValue (Seconds (2.0)),
                   MakeTimeAccessor (&Ipv4TexcpRouting::m_updateLossInterval),
                   MakeTimeChecker ())    
     .AddTraceSource ("UpdateWeight", "Update weight values.",
                  MakeTraceSourceAccessor (&Ipv4TexcpRouting::m_traceUpdate))
     .AddAttribute ("SplittingMode", 
                   "Whether to use Bytes (see MaxBytes) or Packets (see MaxPackets) as the maximum queue size metric.",
                   EnumValue (PREF),
                   MakeEnumAccessor (&Ipv4TexcpRouting::SetSplittingMode),
                   MakeEnumChecker (FLARE, "Flare",
                                    PREF, "Pref"))
     .AddAttribute ("WeightUpdateFunc", "A callback to the function that do the update of weights",
                   CallbackValue (),
                   MakeCallbackAccessor (&Ipv4TexcpRouting::m_weightUpdate),
                   MakeCallbackChecker ())

       ;
  return tid;
}

Ipv4TexcpRouting::Ipv4TexcpRouting () 
: m_ipv4 (0),
  m_probeTimer (Timer::CANCEL_ON_DESTROY),
  m_decisionTimer(Timer::CANCEL_ON_DESTROY)
  //m_classifier(0)
  //m_socket (0)
  
{
  NS_LOG_FUNCTION_NOARGS ();
  m_probeInterval = Seconds (0.1);
  m_externalInterfaces.clear();
  //NS_LOG_INFO("Constructor");
}

void
Ipv4TexcpRouting::SetSplittingMode(SplittingMode mode)
{
   m_sMode = mode;
}

void
Ipv4TexcpRouting::UpdateLossStats()
{                   
  for (PairPathsI sp = m_iePairPaths.begin(); sp != m_iePairPaths.end(); sp++)
   {
     NetworkRoutes *p = (*sp).second;
         for (NetworkRoutesI j = p->begin (); 
              j != p->end (); j++) 
           {

              (*j)->CalculateLoss();
               m_traceUpdate(*j); 
           }
   }  
  /*for (RouteTableI i = m_routeList.begin (); i != m_routeList.end (); i++) 
  {
    (*i)->CalculateLoss();
    m_traceUpdate(*i);
  }*/
  Simulator::Schedule( m_updateLossInterval, &Ipv4TexcpRouting::UpdateLossStats, this);
}
 
Ptr<Ipv4TexcpTableEntry>
Ipv4TexcpRouting::AddNetworkRouteTo (Ipv4Address network, 
                                      Ipv4Mask networkMask,
                                      uint32_t interface, 
                                      struct IEPair s,
                                      DataRate r)
{
  NS_LOG_FUNCTION_NOARGS ();
  NetworkRoutes*  pairPaths = m_iePairPaths[s];

  if ( !pairPaths )
    {
      pairPaths = new NetworkRoutes;
      m_iePairPaths[s] = pairPaths;
    } 
  
  //NS_LOG_INFO("Add Network");
  Ipv4TexcpTableEntry *route = new Ipv4TexcpTableEntry ();
  *route = Ipv4TexcpTableEntry::CreateNetworkRouteTo (network,
                                            networkMask,
                                            interface,
                                            s);

  NS_LOG_INFO("Add network " << network << " IE Pair " << s);
  pairPaths->push_back (route);
  m_externalInterfaces.push_back (interface);
  
  //Set the traffic shaper for this route
  Ptr<TrafficShaperNetDevice> ts = DynamicCast<TrafficShaperNetDevice> (m_ipv4->GetNetDevice (interface));
  //NS_ASSERT(ts);
  if ( ts != 0)
    {
      int shaperIdx = GetNextPathShaperId ();
      route->SetPathShaperId(shaperIdx);
      ts->AddPathShaper(shaperIdx);
      DataRate bps = ts->SetDataRate(r, shaperIdx);
      route->SetAllowedPathRate(bps.GetBitRate());
    }
  return route;
}

struct IEPair 
Ipv4TexcpRouting::LookupIEPair (Ipv4Address dest)
{
  NS_LOG_FUNCTION_NOARGS ();
  struct IEPair   pair = {Ipv4Address::GetAny(), Ipv4Address::GetAny()};
  for (PairPathsI s = m_iePairPaths.begin(); s != m_iePairPaths.end(); s++)
    {
      Ipv4TexcpTableEntry* route = 0;
      route =  LookupTexcp (dest, (*s).second);
      pair = (*s).first;
      if ( route ) return pair;
    }

  return pair;      
}

Ipv4TexcpTableEntry* 
Ipv4TexcpRouting::LookupTexcp (Ipv4Address dest, NetworkRoutes* routeTable )
{
  NS_LOG_FUNCTION_NOARGS ();
  if (!routeTable)
    return 0;

  uint16_t longest_mask = 0;
  Ipv4TexcpTableEntry* selectedRoute = 0;

  for (NetworkRoutesI i = routeTable->begin (); 
      i != routeTable->end (); i++) 
      {
        Ipv4Mask mask = (*i)->GetDestNetworkMask ();
        uint16_t masklen = mask.GetPrefixLength ();
        Ipv4Address entry = (*i)->GetDestNetwork ();
        NS_LOG_LOGIC ("Searching for route to " << dest << ", checking against route to " << entry << "/" << masklen);
        if (mask.IsMatch (dest, entry)) 
        {
          NS_LOG_LOGIC ("Found global network route " << *i << ", mask length " << masklen );
          if (masklen < longest_mask) // Not interested if got shorter mask
          {
            NS_LOG_LOGIC ("Previous match longer, skipping");
            continue;
          }
        longest_mask = masklen;
        selectedRoute = *i;
        } 
      }

  return selectedRoute;
}

 
Ipv4TexcpTableEntry*
Ipv4TexcpRouting::LookupTexcpEntry (IEPair pair, uint32_t idx)
{
  NS_LOG_FUNCTION_NOARGS ();
  for (PairPathsI sp = m_iePairPaths.begin(); sp != m_iePairPaths.end(); sp++)
   {
     struct IEPair s = (*sp).first;
     NetworkRoutes *p = (*sp).second;
     if (s.egressAddr == pair.egressAddr)
       { 
         for (NetworkRoutesI j = p->begin (); 
              j != p->end (); j++) 
           {
             if ((*j)->GetInterface()==idx) return (*j);
           }
       }
   } 
 return 0; 
}


uint32_t 
Ipv4TexcpRouting::GetNRoutes (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  uint32_t nRoutes = 0;
  for (PairPathsI s = m_iePairPaths.begin(); s != m_iePairPaths.end(); s++)
    {
      nRoutes = (*s).second->size();
    }
     
  return nRoutes;
}

Ipv4TexcpTableEntry 
Ipv4TexcpRouting::GetRoute (uint32_t index)
{
  NS_LOG_FUNCTION_NOARGS ();
      //uint32_t tmp = 0;
  /*for (NetworkRoutesI j = m_defaultRoutes.begin (); j != m_defaultRoutes.end (); j++) 
        {
          if (tmp  == index)
          {
            return (*j);
          }
          tmp++;
        }*/
  NS_ASSERT (false);
  // quiet compiler.
  return 0;
}

void 
Ipv4TexcpRouting::RemoveRoute (uint32_t index)
{
  NS_LOG_FUNCTION_NOARGS ();
  /*uint32_t tmp = 0;
  for (NetworkRoutesI j = m_defaultRoutes.begin (); 
       j != m_defaultRoutes.end (); 
       j++) 
    {
      if (tmp == index)
        {
          delete *j;
          m_defaultRoutes.erase (j);
          return;
        }
      tmp++;
    }*/
  NS_ASSERT (false);
}

Ptr<Ipv4Route> 
Ipv4TexcpRouting::RouteOutput (Ptr<Packet> p, const Ipv4Header &header, Ptr<NetDevice> oif, Socket::SocketErrno &sockerr)
{
  NS_LOG_FUNCTION_NOARGS ();
  /*TexcpHeader tt;
  UdpHeader uh;
  p->RemoveHeader(uh);
  p->RemoveHeader(tt) ;
  p->AddHeader(tt);
  p->AddHeader(uh); */

  TexcpTag tag;
  if (p->PeekPacketTag(tag))  
    {
      //NS_LOG_INFO("Route Output "<<header); 
      Ptr<Ipv4Route> rtentry = 0;
      uint32_t interfaceIdx = tag.GetInterfaceIdx ();
      rtentry = Create<Ipv4Route> ();
      rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx)); 
      rtentry->SetGateway (Ipv4Address::GetAny());/////////This a hack, the case where the next hope Address is not specified is not yet implemented, apparently it is enough to specify the interface and set the GatewayAddress for any value for this to work
      if (rtentry)
        {
          sockerr = Socket::ERROR_NOTERROR;
          return rtentry;
        }
    }

  return 0;
}

bool 
Ipv4TexcpRouting::RouteInput  (Ptr<const Packet> p, const Ipv4Header &ipHeader, Ptr<const NetDevice> idev,
                             UnicastForwardCallback ucb, MulticastForwardCallback mcb,
                             LocalDeliverCallback lcb, ErrorCallback ecb)
{
   NS_LOG_FUNCTION (this << p << ipHeader << ipHeader.GetSource () << ipHeader.GetDestination () << idev);

  NS_ASSERT (m_ipv4 != 0);
  // Check if input device supports IP 
  NS_ASSERT (m_ipv4->GetInterfaceForDevice (idev) >= 0);

  //Texcp
  if (m_agentType == 0) return false;  
  //NS_LOG_INFO("Route Input"); 
  int32_t iif = m_ipv4->GetInterfaceForDevice (idev); 
  //Texcp
  if (m_agentType == 0) return false;  
  //NS_LOG_INFO("Route Input"); 
  // Texcp only handles unicast
  if (ipHeader.GetDestination ().IsMulticast () || (ipHeader.GetDestination ().IsBroadcast ()))
    {
      return false;
    }

  //Check if we should use PREF
  if (m_sMode == PREF)
    {
      Ipv4HeaderReflexTag rtag;
      rtag.GetIpv4Header (&ipHeader);

      // check if re-ecn capable
      if ( ! rtag.GetReflex() )
        {
          return false;
        }
      else 
        {
           NS_LOG_LOGIC ("Unicast destination, re-ecn enabled");
        }

      // Check if input device supports IP forwarding
      if (m_ipv4->IsForwarding (iif) == false)
        {
          NS_LOG_LOGIC ("Forwarding disabled for this interface");
          ecb (p, ipHeader, Socket::ERROR_NOROUTETOHOST);
          return false;
        }

      Ptr<Ipv4TexcpTableEntry> selectedRoute;

      if ( IsExternalInterface(iif) ) 
        {

          //for incoming SYN/ACK packets to local domain piggy back route information
          if ( rtag.GetReflex() == Ipv4HeaderReflexTag::FNE )
          {
            //reverse lookup

            struct IEPair pair;
            struct IEPair nullPair = {Ipv4Address::GetAny(), Ipv4Address::GetAny()};
            pair  = LookupIEPair(ipHeader.GetSource());
            if (pair == nullPair) 
              {
                NS_LOG_INFO("False"); 
                return false;
              }

            uint32_t path = SelectPath (pair, 1);
            NetworkRoutes* pathRoutes = m_iePairPaths[pair];
            path = pathRoutes->at(path)->GetInterface(); 
            if ( path )
              {
                NS_LOG_LOGIC ("Tagging incoming packet with route to " << ipHeader.GetSource() << " with path " << path);
                Ipv4HeaderPathTag tag;
                tag.SetPath(path);
                p->AddPacketTag(tag);
              }    
          }

          //retrieve default path for this packet
          return false;
        } 
        else {
        //if packet from internal interface (may or may not be outgoing)
        //NS_LOG_LOGIC ("From ingress link");

        // for packets with codepoint, verify table and forward.
        Ipv4HeaderPathTag ptag;
        ptag.GetIpv4Header(&ipHeader);

        struct IEPair pair;
        struct IEPair nullPair = {Ipv4Address::GetAny(), Ipv4Address::GetAny()};
        pair  = LookupIEPair(ipHeader.GetDestination());
        if (pair == nullPair) 
          {
            NS_LOG_INFO("False"); 
            return false;
          } 
        uint8_t dscp = ptag.GetPath();
        //selectedRoute = LookupReact (ipHeader.GetDestination (), dscp);
        selectedRoute = LookupTexcpEntry (pair, dscp);
    
        // we don't clear dscp right now
        if (dscp) {
          p->AddPacketTag(ptag);
        }
     }


     // Next, try to find a route
     if (selectedRoute)
       {
         Ptr<Ipv4Route> rtentry = 0;
         uint32_t interfaceIdx = selectedRoute->GetInterface ();
         //uint32_t interfaceIdx = 10;
         //std::cout << "Selected Route" << '\n';
         rtentry = Create<Ipv4Route> ();
         rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx));

         int pathShaperIdx = selectedRoute->GetPathShaperId ();
         PathShaperTag stag; 
         stag.SetPathId(pathShaperIdx);
         p->AddPacketTag(stag); 

         //NS_LOG_LOGIC ("Found unicast destination- calling unicast callback");
         ucb (rtentry, p, ipHeader);  // unicast forwarding callback
         //update stats

         selectedRoute->UpdateReflex(rtag.GetReflex(), p->GetSize());

         return true;
       }
     else
       {
         NS_LOG_LOGIC ("Did not find unicast destination- returning false");
         return false; // Let other routing protocols try to handle this
       }
     }

     /////////////Flare Splitting
     else {

      // Check if input device supports IP forwarding
       if (m_ipv4->IsForwarding (iif) == false)
         {
           NS_LOG_LOGIC ("Forwarding disabled for this interface");
           ecb (p, ipHeader, Socket::ERROR_NOROUTETOHOST);
           return false;
         }
       if (IsExternalInterface(iif)) return false;  

       //NS_LOG_LOGIC ("Unicast destination, re-ecn enabled");
       Ipv4TexcpTableEntry* selectedRoute;
       // first find the egress address
       struct IEPair pair;
       struct IEPair nullPair = {Ipv4Address::GetAny(), Ipv4Address::GetAny()};
       pair  = LookupIEPair(ipHeader.GetDestination());
       if (pair == nullPair) 
         {
           NS_LOG_INFO("False"); 
           return false;
         }  

       Ptr<Ipv4TexcpTableEntry> currentRoute = 0;
       NetworkRoutes* pathRoutes = m_iePairPaths[pair];
       //NS_LOG_INFO("Packet for " << pair);
       uint32_t pSize = p->GetSize();
       for ( uint32_t i=0 ; i < pathRoutes->size() ; i++ )
           {
             currentRoute = pathRoutes->at(i);
             if (currentRoute)
               {
                 //double token = currentRoute-> GetToken();
                 currentRoute->UpdateTokenCounter(pSize);
                 //NS_LOG_INFO("Packet coming update path " << i << "Prev token " << token << " Token " << currentRoute->GetToken());
               } 
           }


       Callback<int, struct IEPair, uint32_t> selectRouteCB;
       selectRouteCB = MakeCallback(&Ipv4TexcpRouting::SelectPath, this);
       int pathIdx;
       m_classifier.Classify(ipHeader, p, selectRouteCB, &pathIdx, pair);
       selectedRoute = pathRoutes->at(pathIdx);
       //selectedRoute = pathRoutes->at(2);
       //uint32_t pSize = p->GetSize();
       //NS_LOG_INFO("False"); 
       //NS_LOG_INFO("Path Idx " << pathIdx << " Token " << selectedRoute->GetToken());
       selectedRoute->SelectPath(pSize);
       //NS_LOG_INFO("After Update " << pathIdx << " Token " << selectedRoute->GetToken());
  
  
       // Next, try to find a route
       if (selectedRoute)
         {
           Ptr<Ipv4Route> rtentry = 0;
           uint32_t interfaceIdx = selectedRoute->GetInterface ();
 
           Ipv4HeaderPathTag tag;
           tag.SetPath(interfaceIdx);
           p->AddPacketTag(tag);
           //////////////////////
           ///Tag For The shaper
           int pathShaperIdx = selectedRoute->GetPathShaperId ();
           PathShaperTag stag; 
           stag.SetPathId(pathShaperIdx);
           p->AddPacketTag(stag); 

           rtentry = Create<Ipv4Route> ();
           rtentry->SetOutputDevice (m_ipv4->GetNetDevice (interfaceIdx)); 

           //NS_LOG_LOGIC ("Found unicast destination- calling unicast callback");
           ucb (rtentry, p, ipHeader);  // unicast forwarding callback


           // check if re-ecn capable
           Ipv4HeaderReflexTag rtag;
           rtag.GetIpv4Header (&ipHeader);

           if ( rtag.GetReflex() )
             {
                 selectedRoute->UpdateReflex(rtag.GetReflex(), p->GetSize());
             }


           return true;
          }
        else
          {
            NS_LOG_LOGIC ("Did not find unicast destination- returning false");
            return false; // Let other routing protocols try to handle this
          } 
}
}

Ipv4TexcpRouting::~Ipv4TexcpRouting ()
{
  NS_LOG_FUNCTION_NOARGS ();
}

void
Ipv4TexcpRouting::DoDispose (void)
{
  NS_LOG_FUNCTION_NOARGS ();

  //for each interface table, delete routing entries, then table
  /*for (InterfaceRoutingTablesI ifTable = m_interfaceTables.begin (); ifTable !=
    m_interfaceTables.end(); ifTable = m_interfaceTables.erase (ifTable) )
  {
    if( *ifTable )
    {
      for (NetworkRoutesI j = (*ifTable)->begin (); j != (*ifTable)->end (); j = (*ifTable)->erase (j)) 
        delete *j;
    }
  }*/

  //for (NetworkRoutesI j = m_defaultRoutes.begin (); j != m_defaultRoutes.end (); j = m_defaultRoutes.erase (j))
   // ;

  m_ipv4 = 0;
  Ipv4RoutingProtocol::DoDispose ();
  /*m_probeTimer.Remove ();
  m_socket->Close ();*/
}

void 
Ipv4TexcpRouting::DoStart (int mode)
{
  NS_LOG_FUNCTION_NOARGS ();
  m_agentType = mode;
  Ptr<Node> n =m_ipv4->GetObject<ns3::Node> ();
  NS_ASSERT( n != 0);
  UpdateLossStats(); 
  //m_classifier = Ipv4FlowletClassifier();
  struct IEPair ie; 
  for (PairPathsI s = m_iePairPaths.begin(); s != m_iePairPaths.end(); s++)
    {
      //NS_LOG_INFO("Do start " << (*s).first);
      NetworkRoutes * routeTable = (*s).second;
      if (routeTable)
        {
          /////// calculate the average utilization
          double weight = 1/((double)routeTable->size());
          //NS_LOG_INFO("IE Pair " << (*s).first << " Weight " << weight);
          double k = 0;
          double tot = 0;
          IntEmpiricalVariable* iev = new IntEmpiricalVariable();     
          for (NetworkRoutesI j = routeTable->begin (); 
                j != routeTable->end (); j++) 
            {
              //Set the weight, by default all the routes are eaqual at the begining
              //Ptr<Ipv4TexcpTableEntry> stats = (*j)->GetTexcpPathState();
              (*j)->SetWeight(weight);
              //set the random variable
              tot+=weight;
              iev->CDF(k,tot);
              k++;
              //NS_LOG_INFO(weight);
            }
           m_iePairRand[(*s).first] = iev;
           ie = (*s).first;
           }
     }      //Craet the socket
           Ptr <Socket> socket = Socket::CreateSocket (n, UdpSocketFactory::GetTypeId());
           socket->SetRecvCallback (MakeCallback (&Ipv4TexcpRouting::RecvTexcpProbe, this));
           //socket->Bind (InetSocketAddress ((*s).first.ingressAddr, TEXCP_PORT_NUMBER));
           socket->Bind (InetSocketAddress (ie.ingressAddr, TEXCP_PORT_NUMBER));
           //socket->Connect(InetSocketAddress((*s).first.egressAddr, TEXCP_PORT_NUMBER));  
           //socket->SetRecvCallback(MakeCallback (&Ipv4TexcpRouting::RecvTexcpProbe, this));
           //m_socketTableEntries[socket] = (*j);
           //m_pairSocket[(*s).first] = socket;
           ie.egressAddr = ie.ingressAddr;
           //NS_LOG_INFO(ie);
           m_pairSocket[ie] = socket;
           //NS_LOG_INFO((*j) -> GetEgressAddress());  
                 
        //}
   // }
  m_decisionTimer.SetFunction(&Ipv4TexcpRouting::DecisionTimerExpire, this);
  m_probeTimer.SetFunction (&Ipv4TexcpRouting::ProbeTimerExpire, this);
  if (mode ==1 ) {m_decisionTimer.Schedule(Seconds(5*m_probeInterval.GetSeconds()));
  m_probeTimer.Schedule(m_probeInterval);}

     for (uint32_t i=0; i < m_ipv4->GetNInterfaces (); i++)
        {
          Ipv4Address addr = m_ipv4->GetAddress (i,0).GetLocal ();
          //NS_LOG_INFO(addr);

               Ptr <Socket> socket2 = Socket::CreateSocket (n, UdpSocketFactory::GetTypeId());
                  socket2->SetRecvCallback (MakeCallback (&Ipv4TexcpRouting::RecvTexcpProbe, this));
                  socket2->Bind (InetSocketAddress (addr, TEXCP_PORT_NUMBER));
                  //socket2->SetRecvCallback(MakeCallback (&Ipv4TexcpRouting::RecvTexcpProbe, this));
         }
  m_classifier.SetTimeOut(m_flowletTimeOut);
    
}
void 
Ipv4TexcpRouting::NotifyInterfaceUp (uint32_t i)
{
  NS_LOG_FUNCTION_NOARGS ();
}

void 
Ipv4TexcpRouting::NotifyInterfaceDown (uint32_t i)
{
  NS_LOG_FUNCTION_NOARGS ();
  // TODO  
  /*uint32_t j = 0;
  while (j < GetNRoutes())
    {
      Ipv4TexcpTableEntry route = GetRoute (j);
      if (route.GetInterface () == i)
        {
          RemoveRoute (j);
        }
      else
        {
          j++;
        }
    }*/
}

void 
Ipv4TexcpRouting::NotifyAddAddress (uint32_t interface, Ipv4InterfaceAddress address)
{
  NS_LOG_FUNCTION (this << interface);

  //Not needed; if the new interface will allow a new path there will be a need to extra information in order to be used here 
}

void 
Ipv4TexcpRouting::NotifyRemoveAddress (uint32_t interface, Ipv4InterfaceAddress address)
{
  NS_LOG_FUNCTION_NOARGS ();
  if (!m_ipv4->IsUp (interface))
    {
      return;
    }
  Ipv4Address networkAddress = address.GetLocal ().CombineMask (address.GetMask ());
  Ipv4Mask networkMask = address.GetMask ();
  // Remove all static routes that are going through this interface
  // which reference this network
  for (uint32_t j = 0; j < GetNRoutes (); j++)
    {
      Ipv4TexcpTableEntry route = GetRoute (j);
      if (route.GetInterface () == interface &&
          route.GetDestNetwork () == networkAddress &&
          route.GetDestNetworkMask () == networkMask)
        {
          RemoveRoute (j);
        }
    }
}

void 
Ipv4TexcpRouting::SetIpv4 (Ptr<Ipv4> ipv4)
{
  NS_LOG_FUNCTION (this << ipv4);
  NS_ASSERT (m_ipv4 == 0 && ipv4 != 0);
  m_ipv4 = ipv4;
  /*if( m_interfaceTables.size() < m_ipv4->GetNInterfaces () )
    m_interfaceTables.resize( m_ipv4->GetNInterfaces () );*/

}


bool
Ipv4TexcpRouting::IsExternalInterface(uint32_t ifnum)
{
  NS_LOG_FUNCTION (ifnum);
  for (std::list<uint32_t>::iterator i = m_externalInterfaces.begin(); 
       i != m_externalInterfaces.end(); i++)
    {
       if ( (*i)==ifnum)
      return true;
    }
  return false;
}

void
Ipv4TexcpRouting::RecvTexcpProbe (Ptr<Socket> socket)
{
 NS_LOG_FUNCTION(this << socket);
 Ptr<Packet> packet;
 Address from;
 while(packet = socket->RecvFrom (from))
   {
     if (InetSocketAddress::IsMatchingType (from))
       {
         InetSocketAddress address = InetSocketAddress::ConvertFrom (from);
         //NS_LOG_INFO ("Received " << packet ->GetSize() << " bytes from " << address.GetIpv4());

         TexcpHeader th;
         packet->RemoveHeader(th);
         if (th.GetState())
           {
             //NS_LOG_INFO ("Received Probe from " << address.GetIpv4());
             th.SetState(0);
             //NS_LOG_INFO(th.GetPathUtilization());
             packet->RemoveAllPacketTags ();
             packet->RemoveAllByteTags ();
                
         
             //NS_LOG_INFO(th.GetState());
             packet->AddHeader(th);
             NS_LOG_LOGIC ("Echoing packet");
             socket->SendTo (packet, 0, from);       
           }
        else
          {
            NS_LOG_INFO("Recieved back");
            //Ipv4TexcpTableEntry *path = m_socketTableEntries[socket];
            Ipv4TexcpTableEntry * path = LookupTexcpEntry(th.GetIEPair(), th.GetInterfaceIdx());
            NS_ASSERT(path !=0); 
            //NS_LOG_INFO(th);
            //NS_LOG_INFO(th.GetIEPair());
            path->UpdateProbe(th);
            DataRate r = DataRate(path->GetAllowedPathRate());
            //DataRate r = DataRate("5Mbps");
            Ptr<TrafficShaperNetDevice> ts = DynamicCast<TrafficShaperNetDevice> (m_ipv4->GetNetDevice (path->GetInterface()));
            if (ts)
              {
                r = ts->SetDataRate(r, path->GetPathShaperId());
                path->SetAllowedPathRate(r.GetBitRate());
                NS_LOG_INFO("Update interface "<<  path->GetInterface() << " Shapet Id " << path->GetPathShaperId() << " NEw rate " << path->GetAllowedPathRate());
              }
          }
        }
   }

}

void 
Ipv4TexcpRouting::ProbeTimerExpire ()
{
  NS_LOG_FUNCTION_NOARGS ();
 //NS_LOG_INFO("Expire");
 for (PairPathsI s = m_iePairPaths.begin(); s != m_iePairPaths.end(); s++)
   {
     for (NetworkRoutesI j = (*s).second->begin (); 
      j != (*s).second->end (); j++) 
       {

         struct IEPair ie = {(*s).first.ingressAddr , (*s).first.ingressAddr};
         //NS_LOG_INFO(ie);
         Ptr<Socket> socket = m_pairSocket[ie];
         Ptr<Packet> p = (*j)->GetProbe ();        
         //SendPacket(p,socket, InetSocketAddress((*s).first.egressAddr, TEXCP_PORT_NUMBER));
         NS_ASSERT(p != 0 && socket !=0);
         socket->SendTo (p, 0, InetSocketAddress((*s).first.egressAddr, TEXCP_PORT_NUMBER));

       }
   }
 /*for (SockTableI i = m_socketTableEntries.begin(); i != m_socketTableEntries.end(); i++)
   {
      Ipv4TexcpTableEntry * path = (*i).second;
      NS_ASSERT(path !=0); 
      //SetProbe(path);
      //NS_LOG_INFO ("Probe");

      Ptr<Packet> p = path ->GetProbe();
      SendPacket(path->GetProbe(), (*i).first);
      //NS_LOG_INFO("Send");

    }*/

 //////////// TODO think about Probe synchronisation
 m_probeTimer.Schedule(m_probeInterval);
}

void
Ipv4TexcpRouting::DecisionTimerExpire ()
{
  NS_LOG_FUNCTION_NOARGS ();
 //NS_LOG_INFO("Decision Expire");
  Time decisionInt = Seconds(5.0*m_probeInterval.GetSeconds());
  for (PairPathsI s = m_iePairPaths.begin(); s != m_iePairPaths.end(); s++)
   {
     /////// calculate the average utilization
     double avgU = 0;
     double tot = 0;
     tot = m_weightUpdate((*s).second, avgU, tot );

     /*tot = 0.0;
     for (NetworkRoutesI j = (*s).second->begin (); 
      j != (*s).second->end (); j++) 
       {
         //Ptr<TexcpPathState> stats = (*j)->GetTexcpPathState();
         avgU += (*j)->GetPathUtilization()* (*j)->GetWeight();
         //NS_LOG_INFO("Path utilization " << stats ->GetPathUtilization() << "Weight " << stats -> GetWeight() );
       }
     //// calculate the new weight
     //NS_LOG_INFO("Path utilization average " << avgU);
     for (NetworkRoutesI j = (*s).second->begin (); 
      j != (*s).second->end (); j++) 
       {
         //Ptr<TexcpPathState> stats = (*j)->GetTexcpPathState();
         double weight = 0;
         weight = (*j)->GetWeight()*(avgU - (*j)->GetPathUtilization());//dx_sp+x_sp = x_sp*(avgU - u_sp)
         //NS_LOG_INFO(weight);
         weight += (*j)->GetWeight();
         if (weight < 0.1)  weight = 0.1;
             
             (*j)->SetWeight(weight);
             tot += weight;
         //NS_LOG_INFO((*j)->GetWeight());
       }*/
     double k = 0;
     double cumWeight = 0;
     //IntEmpiricalVariable* iev = new IntEmpiricalVariable();     
     for (NetworkRoutesI j = (*s).second->begin (); 
      j != (*s).second->end (); j++) 
       { 
         //Ptr<TexcpPathState> stats = (*j)->GetTexcpPathState();
         double weight = (*j)->GetWeight();
         NS_ASSERT(tot != 0);//To be removed
         weight = weight/tot;
         (*j)->SetWeight(weight);

         Ipv4Address addr = m_ipv4->GetAddress ((*j)->GetInterface (), 0).GetLocal ();
         if (m_agentType ==1) NS_LOG_INFO("Interface Address " << addr << " weight " << weight  << " Average " << avgU << " Path Utilization " << (*j)->GetPathUtilization());
         //NS_LOG_INFO("Normalized weight " << (weight));
         //Set the new values for the CDF
         cumWeight+=weight;
         //iev->CDF(k,cumWeight);
         k++;

         //if (m_sMode == FLARE)
         //  {
              //Reset the Token counter
              //(*j)->ResetToken();
         //  }   
         m_traceUpdate(*j);
       }
    //m_iePairRand[(*s).first] = iev;
    } 
 m_decisionTimer.Schedule(decisionInt);
}

/*void
Ipv4TexcpRouting::SendPacket(Ptr<Packet> packet, Ptr<Socket> socket, InetSocketAddress egress)
{
  NS_LOG_FUNCTION_NOARGS ();

 NS_ASSERT(packet != 0 && socket !=0);
 socket->SendTo (packet,0 , Address( egress));
 //NS_LOG_INFO ("Sent to ");// << m_socketTableEntries[socket]->GetEgressAddress());

 / *TexcpHeader tt;
 packet->PeekHeader(tt)* /;

}*/

/*void
Ipv4TexcpRouting::LogRoutingTable (const NetworkRoutes *routeTable)
{
   for (NetworkRoutesCI i = routeTable->begin (); i != routeTable->end (); i++) 
    {
      NS_LOG_FUNCTION(this << *i);
    }
}*/

void
Ipv4TexcpRouting::LogRoutingTables ()
{
  NS_LOG_FUNCTION("Default Routes");
  //LogRoutingTable( &m_defaultRoutes );
  /*for ( unsigned int i = 0; i< m_interfaceTables.size(); i++ )
  {
    if ( m_interfaceTables[i] )
    {
      NS_LOG_FUNCTION ( "Interface routes for " << i);
      LogRoutingTable( m_interfaceTables[i] );
    }
  }*/

}

/*void
Ipv4TexcpRouting::SetProbe (Ipv4TexcpTableEntry* path)
{
  NS_LOG_FUNCTION_NOARGS ();
 / *TexcpHeader th;
 th.SetInterfaceIdx(path->GetInterface ());
 th.SetState(1);
 TexcpTag tag;
 tag.SetInterfaceIdx(path->GetInterface ());
 Ptr<Packet> probe = Create<Packet> ();
 probe->AddPacketTag(tag);
 NS_LOG_INFO("Create packet");
 probe->AddHeader(th);
 m_egressProbes[path]= probe;* /

}*/

int
Ipv4TexcpRouting::SelectPath (struct IEPair s, uint32_t size)   
{
  NS_LOG_FUNCTION_NOARGS ();

  //NS_LOG_INFO("SelectPath");
  Ptr<Ipv4TexcpTableEntry> selectedRoute = 0;
  Ptr<Ipv4TexcpTableEntry> currentRoute = 0;

  double maxToken = 0;
  double curToken = 0;
  int hasToken = 0;
  int curIdx;

  NetworkRoutes* pathRoutes = m_iePairPaths[s];
  //get all routes, choose route with highest token
  for ( uint32_t i=0 ; i < pathRoutes->size() ; i++ )
  {
    currentRoute = pathRoutes->at(i);
    if(currentRoute)
    {
      if (m_sMode == PREF)
        {
          currentRoute->UpdateTokenCounter(1);
        }
      curToken = currentRoute->GetToken();

      //NS_LOG_INFO( s << " " << i << " " << curToken);
      if(!hasToken) {
        hasToken = 1;
        maxToken = curToken;
        selectedRoute = currentRoute;
        curIdx = i;
      }
      else if ( maxToken < curToken ) {
        maxToken = curToken;
        selectedRoute = currentRoute;
        curIdx = i;
      }
    }
  }

  if(selectedRoute)
  {
    //NS_LOG_INFO( "maxToken" << maxToken << " using "<< selectedRoute->GetInterface() << '\n' );
    // update token data for selected path
    if (m_sMode == PREF)
      {
        selectedRoute->SelectPath(1);
      }
    //return selectedRoute->GetInterface();
    return curIdx;
  }

  NS_LOG_INFO("No ROUTE?");
  
  return 0;
}
}}//namespace ns3
